#!usr/bin/python
from __future__ import division

import random

import pygame
pygame.init()

import text

from locals import *
import maps
import objects

keys_down = {
	pygame.K_UP: False,
	pygame.K_DOWN: False,
	pygame.K_LEFT: False,
	pygame.K_RIGHT: False,
	pygame.K_LSHIFT: False,
}

buttons_down = {
	1: False,
	2: False,
	3: False,
}

font = pygame.font.Font(os.path.join(MOD_DATA_DIR, "font.ttf"), 8)

class EditableMap(maps.Map):
	"""A PyChallenge map in which tiles at a certain position in the map can be
	changed. The map will resize for tiles placed beyond its edges.
	"""
	
	input = None
	
	def _shift(self, movement, default_tile):
		"""Shift the map by movement leaving the given tile behind and increasing
		the size of the map.
		"""
		
		move_x, move_y = movement
		
		width, height = self.size
		
		# Correct tile positions
		for y, row in enumerate(self):
			for x, tile in enumerate(row):
				x, y = tile.position
				tile.position = (x + max(0, move_x), y + max(0, move_y))
				
		# Correct object positions
		for object in self.objects:
			x, y = object.position
			object.position = (x + max(0, move_x), y + max(0, move_y))
		
		if move_x > 0:
			for x in xrange(move_x, 0, -1):
				for y, row in enumerate(self):
					row.insert(0, default_tile(self, (x - 1, y)))
		elif move_x < 0:
			for x in xrange(abs(move_x)):
				for y, row in enumerate(self):
					row.append(default_tile(self, (width + x, y)))
		
		width += abs(move_x)
		
		if move_y > 0:
			for y in xrange(move_y, 0, -1):
				new_row = [default_tile(self, (x, y - 1)) for x in xrange(width)]
				self.map_data.insert(0, new_row)
		elif move_y < 0:
			for y in xrange(abs(move_y)):
				new_row = [default_tile(self, (x, height + y)) for x in xrange(width)]
				self.map_data.append(new_row)
		
		height += abs(move_y)
			
		self.size = width, height
		
		pixel_size = (width * 32, height * 32)
		
		self.tile_render = pygame.Surface(pixel_size).convert()
		self._render_tiles(self.tile_render)
		
		self.object_render = pygame.Surface(pixel_size).convert_alpha()
		self._render_objects(self.object_render)
	
	def _normalise_position(self, position, tile=None):
		if not tile:
			tile = self.tileset["."]
			
		x, y = position
		width, height = self.size
		
		move_x, move_y = (0, 0)
		
		if not 0 <= x < width - 1:
			if x < 0:
				move_x = x
			else:
				move_x = x - (width - 1)
				
		if not 0 <= y < height - 1:
			if y < 0:
				move_y = y
			else:
				move_y = y - (height - 1)
		
		if move_x != 0 or move_y != 0:
			self._shift((0 - move_x, 0 - move_y), tile)

		if move_x < 0:
			x -= move_x
			
		if move_y < 0:
			y -= move_y
			
		return x, y
	
	def strip(self, border=True):
		"""Remove columns/rows containing only the 0th tile.
		
		If border then leave a 1 cell border of 0th tiles.
		"""
		
		blank_rows = 0
		
		for index, row in enumerate(self):
			blank_rows += 1
			
			for tile in row:
				if self.tileset.index(tile.__class__) == 0:
					blank_rows -= 1
					break
					
		if border:
			blank_rows -= 1
			
		for index, row in enumerate(self):
			if index < blank_rows:
				self.map_data.pop(0)
			else:
				break

	def set_tile(self, position, tile):
		"""Set the tile in the map at position. If the position is not within
		the current map: shift the map so it is.
		"""
			
		x, y = self._normalise_position(position, self.tileset["."])
		
		self[y][x] = tile(self, (x, y))
		self._render_tiles(self.tile_render, (x - 1, y - 1, 3, 3))
		
	def add_object(self, position, type):
		x, y = self._normalise_position(position, self.tileset["."])
		
		try:
			object = type(self, (x, y))
			self.objects.append(object)
		except TypeError:
			def add_object(arguments):
				try:
					object = type(self, (x, y), *parse.parse(arguments, parse.variables))
					self.objects.append(object)
				except TypeError:
					print "Incompatiable arguments."
			
			self.input = text.TextEntry("Extra args", function=add_object)
				

class Editor(object):
	def __init__(self):		
		self.window = pygame.display.set_mode((512, 352)) 
		pygame.display.set_caption("pychallenge") 
		self.screen = pygame.display.get_surface()
		
		objects.load_graphics(OBJECT_DIR)
		objects.convert_graphics()
		
		self.viewport = (0, 0, 480, 352)
		
		self.tile_layout = (
			('.','#'),
			('_','~'),
			('@','e'),
			('?','!'),
			('>','v'),
			('<','^'),
			('o','+'),
			('r','\\'),
			('l','/'),
			('x','&'),
			(':',':'),
		)
		
		self.object_layout = (
			("player", "hint"),
			("chip door", "chip"),
			("red door", "red key"),
			("blue door", "blue key"),
			("green door", "green key"),
			("yellow door", "yellow key"),
			("fireball", "ball"),
			("block", "block"),
			("fire", "bomb"),
			("toggle wall", "toggle button"),
			("recessed wall", "teleport"),
		)
		
		self.level = text.TextEntry("Edit map").input(self.screen)
		self._restart_map()
		
		self.palette = 0
		self.tile = self.tileset["#"]
		self.object = objects.names["player"]
		
		self.input = None
	
	def _change_map(self, map):
		self.map = map
		
		self.events = []
		for key in keys_down:
			keys_down[key] = False
		
		self.frame = 0
		
		for object in self.map.objects:
			if object.__class__.__name__ == "player":
				self.player = object
				break
	
	def _restart_map(self):
		path = os.path.join(LEVEL_BASE_DIR, self.level) + ".pcl"
		self._change_map(EditableMap.load_map(open(path, 'r')))
		self.tileset = self.map.tileset
		self.tile = self.tileset["#"]
		self.object = objects.names["player"]
		
		width, height = self.map.size
		centre_x, centre_y = ((width * 32) / 2, (height * 32) / 2)
		
		x, y, width, height = self.viewport
		self.viewport = (centre_x - (width / 2), centre_y - (height / 2), width, height)
		
	def _next_map(self):
		self.level += 1
		self._restart_map()
	
	def update(self):		
		if self.map.input:
			self.input = self.map.input
			self.map.input = None
		
		if self.input:
			if self.input.done:
				self.input = None
		
		for event in self.events:
			self.event(event)
		
		self.events = []
		
		for movement in self.check_controls():
			speed = 4
			if keys_down[pygame.K_LSHIFT]:
				speed *= 8
			
			move_x, move_y = movement
			
			x, y, width, height = self.viewport
			x += move_x * speed
			y += move_y * speed
			self.viewport = x, y, width, height
			
		if buttons_down[1]:
			x, y = pygame.mouse.get_pos()
			
			if x < 448:
				view_x, view_y, view_width, view_height = self.viewport
	
				mouse_x_offset = int(((x + view_x % 32) // 32) + view_x // 32)
				mouse_y_offset = int(((y + view_y % 32) // 32) + view_y // 32)
				
				if self.palette == 0:
					self.map.set_tile((mouse_x_offset, mouse_y_offset), self.tile)
				elif self.palette == 1:
					buttons_down[1] = False
					self.map.add_object((mouse_x_offset, mouse_y_offset), self.object)
					
				if mouse_x_offset < 0:
					view_x -= mouse_x_offset * 32
				if mouse_y_offset < 0:
					view_y -= mouse_y_offset * 32
					
				self.viewport = (view_x, view_y, view_width, view_height)
			else:
				if self.palette == 0:
					self.tile = self.tileset[self.tile_layout[y // 32][(x - 448) // 32]]
				elif self.palette == 1:
					self.object = objects.names[self.object_layout[y // 32][(x - 448) // 32]]
		
	def draw(self):		
		self.screen.fill((32, 32, 32))
		
		map_surface = self.screen.subsurface((0, 0, 448, 352))
		palette_surface = self.screen.subsurface((448, 0, 64, 352))
		
		self.map.draw(map_surface, self.viewport)
		
		palette_surface.fill((32, 32, 32))
		
		if self.palette == 0:
			for y, row in enumerate(self.tile_layout):
				for x, tile_symbol in enumerate(row):
					tile = self.tileset[tile_symbol]
					
					tile(None, (x, y)).draw(palette_surface)
					
					if self.tile == tile:
						pygame.draw.rect(palette_surface, (255, 255, 255), (x * 32, y * 32, 32, 32), 1)
		elif self.palette == 1:
			for y, row in enumerate(self.object_layout):
				for x, object_name in enumerate(row):
					object = objects.names[object_name]
					
					palette_surface.fill((32, 32, 32), (x * 32, y * 32, 32, 32))
					palette_surface.blit(object.graphic, (x * 32, y * 32))
					
					if self.object == object:
						pygame.draw.rect(palette_surface, (255, 255, 255), (x * 32, y * 32, 32, 32), 1)
		
		if self.input:
			self.input.draw(self.screen)
		
		pygame.display.flip()

	def event(self, event):
		if self.input:
			if self.input.event(event):
				return
		
		if event.type == pygame.KEYDOWN or event.type == pygame.KEYUP:	
			keys_down[event.key] = (event.type == pygame.KEYDOWN)
		elif event.type == pygame.MOUSEBUTTONDOWN or event.type == pygame.MOUSEBUTTONUP:
			if not self.input:
				buttons_down[event.button] = (event.type == pygame.MOUSEBUTTONDOWN)
			elif event.type == pygame.MOUSEBUTTONDOWN:
				x, y = pygame.mouse.get_pos()
				
				if x < 448:
					view_x, view_y, view_width, view_height = self.viewport
					
					mouse_x = int(((x + view_x % 32) // 32) + view_x // 32)
					mouse_y = int(((y + view_y % 32) // 32) + view_y // 32)
					
					self.input.content += ", (%s, %s)" % (mouse_x, mouse_y)
		
		if event.type == pygame.KEYDOWN:
			if event.key == pygame.K_s:
				def save(filename):
					#self.map.strip()
					maps.Map.save_map(self.map, open(LEVEL_BASE_DIR + "/%s.pcl" % filename, 'w'))
				
				self.input = text.TextEntry("Save as", function=save)
				
			if event.key == pygame.K_SPACE:
				self.palette += 1
				
				if self.palette > 1:
					self.palette = 0
	
	def check_controls(self):
		movements = []
		
		if keys_down[pygame.K_UP]:
			movements.append((0, -1))
		if keys_down[pygame.K_DOWN]:
			movements.append((0, 1))
		if keys_down[pygame.K_LEFT]:
			movements.append((-1, 0))
		if keys_down[pygame.K_RIGHT]:
			movements.append((1, 0))
			
		return movements
		
def main():
	text.TextEntry.font = pygame.font.Font(os.path.join(MOD_DATA_DIR, "font.ttf"), 8)
	
	EDITOR = Editor()
	
	quit = False
	clock = pygame.time.Clock()
	
	while not quit:
		clock.tick(50)
		pygame.time.wait(10)
		
		for event in pygame.event.get():
			if event.type == pygame.QUIT: 
				quit = True
				
			EDITOR.events.append(event)
		
		EDITOR.update()
		EDITOR.draw()

main()
